#include "line.h"
#include "ExcepLine.h"
#include "line_info.h"

#include <fstream>
#include <iomanip>

namespace tinytool
{
namespace line
{

enum class DealCommentRetType
{
    not_comment,
    unclose_comment,
};

static DealCommentRetType deal_comment(std::string& str)
{
    if (str.find("//") == 0) {
        str = "";
        return DealCommentRetType::not_comment;
    }
    auto len                 = str.size();
    bool block_comment_close = true;
    for (int i = 0; i < len - 1; ++i) {
        if (str[i] == '/') {
            if (str[i + 1] == '/') {
                str = str.substr(0, i);
                len = str.size();
            }
            else if (str[i + 1] == '*') {
                block_comment_close = false;
                for (int j = i + 2; j < len - 1; ++j) {
                    if (str[j] == '*' && str[j + 1] == '/') {
                        block_comment_close = true;
                        str                 = str.substr(0, i) + str.substr(j + 1);
                        str[j + 1]          = ' ';
                        len                 = str.size();
                        break;
                    }
                }
                if (block_comment_close == false) {
                    str = str.substr(0, i);
                    return DealCommentRetType::unclose_comment;
                }
            }
        }
    }
    return DealCommentRetType::not_comment;
}

static DealCommentRetType deal_unclose_comment(std::string& str)
{
    auto len = str.size();
    for (int i = 0; i < len - 1; ++i) {
        if (str[i] == '*') {
            if (str[i + 1] == '/') {
                str    = str.substr(i + 1);
                str[0] = ' ';
                return deal_comment(str);
            }
            break;
        }
    }
    str = "";
    return DealCommentRetType::unclose_comment;
}

static bool is_empty(const std::string& str)
{
    return str == std::string(str.size(), ' ');
}

static line_t single_file_line(const FS::path& path)
{
    std::ifstream ifs(path);
    THROW_EXCEP_LINE_IF(!ifs, "Not open file" << mark_path(path));

    line_t line = 0;
    while (true) {
        std::string str;
        std::getline(ifs, str);
        if (str.empty()) {
            if (ifs.eof()) break;
            continue;
        }
        auto flag = deal_comment(str);
        while (flag != DealCommentRetType::not_comment) {
            if (!is_empty(str)) ++line;
            str = "";
            std::getline(ifs, str);
            if (str.empty()) {
                if (ifs.eof()) break;
                continue;
            }
            flag = deal_unclose_comment(str);
        }
        if (!is_empty(str)) ++line;
    }
    cout_log(LINE_PROGRAM_NAME, "lines: " + mark(line) + " -> " + mark_path(path));
    return line;
}


line_t line(FS::path path)
{
    // path = FS::canonical(FS::current_path() / path);
    THROW_EXCEP_LINE_IF(!FS::exists(path), "No such file or directory" + mark_path(path));
    if (FS::is_regular_file(path)) {
        return single_file_line(path);
    }
    else if (FS::is_directory(path)) {
        line_t line = 0;
        for (auto it : FS::recursive_directory_iterator(path)) {
            if (FS::is_directory(it)) continue;
            line += single_file_line(it);
        }
        return line;
    }
    return 0;
}

}   // namespace line
}   // namespace tinytool
